#include <stdint.h>

#ifndef __M3DPOINT_H
#define __M3DPOINT_H

namespace m3d
{
#pragma pack (push, 1)
	typedef struct SMatrix2x2	SMatrix2;
	typedef struct SMatrix3x3	SMatrix3;
	typedef struct SMatrix4x4	SMatrix4;
	typedef struct SPoint2F		SVector2F;
	typedef struct SPoint3F		SVector3F;
	typedef struct SPoint4F		SVector4F;
	struct SQuaternion;

	typedef struct SPoint2F
	{
		float x, y;

		// constructors
		SPoint2F( void ); 
		SPoint2F( float x, float y ); 

		// -------------------- operators
		// arithmetic
		SPoint2F	operator + ( const SPoint2F& b ) const;
		SPoint2F	operator - ( const SPoint2F& b ) const;
		SPoint2F	operator * ( float _scalar ) const;
		SPoint2F	operator / ( float _scalar ) const;
		// assignment
		SPoint2F&	operator = ( const SPoint2F& b );
		SPoint2F&	operator += ( const SPoint2F& b );
		SPoint2F&	operator -= ( const SPoint2F& b );
		SPoint2F&	operator *= ( float _scalar );
		SPoint2F&	operator /= ( float _scalar );
		// comparison
		bool		operator == ( const SPoint2F& b ) const;
		bool		operator != ( const SPoint2F& b ) const;

		// methods
		float		Length() const;
		double		SqLength( void ) const;
		float		Dot( const SPoint2F& b ) const;
		SPoint2F&	Normalize( void );
		float		AngleWith( const SPoint2F& v ) const;			// angle in rad

	} SVector2F;

	typedef struct SPoint3F
	{ 
		float x, y, z;

		// constructors
		SPoint3F( void ); 
		SPoint3F( float x, float y, float z ); 

		// -------------------- operators
		// arithmetic
		SPoint3F	operator + ( const SPoint3F& b ) const;
		SPoint3F	operator - ( const SPoint3F& b ) const;
		SPoint3F	operator * ( float _scalar ) const;
		SPoint3F	operator / ( float _scalar ) const;
		// assignment
		SPoint3F&	operator = ( const SPoint3F& b );
		SPoint3F&	operator += ( const SPoint3F& b );
		SPoint3F&	operator -= ( const SPoint3F& b );
		SPoint3F&	operator *= ( float _scalar );
		SPoint3F&	operator /= ( float _scalar );
		// comparison
		bool		operator == ( const SPoint3F& b ) const;
		bool		operator != ( const SPoint3F& b ) const;

		// methods
		float		Length() const;
		double		SqLength( void ) const;
		float		Dot( const SPoint3F& b ) const;
		SPoint3F&	Normalize( void );
		float		AngleWith( const SPoint3F& v ) const;			// angle in rad
		SPoint3F	Cross( const SPoint3F& _v2 ) const;
		SPoint3F&	Cross( const SPoint3F& _v1, const SPoint3F& _v2 );

	} SVector3F;

	typedef struct SPoint4F
	{
		float x, y, z, w;

		// constructors
		SPoint4F( void ); 
		SPoint4F( float x, float y, float z, float w=1.0f ); 

		// -------------------- operators
		// arithmetic
		SPoint4F	operator + ( const SPoint4F& b ) const;
		SPoint4F	operator - ( const SPoint4F& b ) const;
		SPoint4F	operator * ( float _scalar ) const;
		SPoint4F	operator / ( float _scalar ) const;
		// assignment
		SPoint4F&	operator = ( const SPoint4F& b );
		SPoint4F&	operator += ( const SPoint4F& b );
		SPoint4F&	operator -= ( const SPoint4F& b );
		SPoint4F&	operator *= ( float _scalar );
		SPoint4F&	operator /= ( float _scalar );
		// comparison
		bool		operator == ( const SPoint4F& b ) const;
		bool		operator != ( const SPoint4F& b ) const;

		// methods
		float		Length() const;
		double		SqLength( void ) const;
		float		Dot( const SPoint4F& b ) const;
		SPoint4F&	Normalize( void );
		float		AngleWith( const SPoint4F& v ) const;			// angle in rad

	} SVector4F;

	struct SQuaternion
	{
		float x, y, z, w;
		SQuaternion( void );
		SQuaternion( float _x, float _y, float _z, float _w );

		// comparison operator
		bool				operator ==( const SQuaternion& q ) const;
		bool				operator !=( const SQuaternion& q ) const;

		// Assignment operators
		const SQuaternion&	operator = ( const SQuaternion& q );
		const SQuaternion&	operator += ( const SQuaternion& q );
		const SQuaternion&	operator -= ( const SQuaternion& q );
		const SQuaternion&	operator *= ( const SQuaternion& q );
		const SQuaternion&	operator *= ( float f );
		const SQuaternion&	operator /= ( float f );
		// 
		SQuaternion			operator +  ( const SQuaternion&q ) const;
		SQuaternion			operator -  ( const SQuaternion&q ) const;
		SQuaternion			operator *  ( const SQuaternion&q ) const;
		SQuaternion			operator *  ( const SVector4F &v ) const;
		SQuaternion			operator *  ( const SVector3F &v ) const;
		SQuaternion			operator *  ( float f ) const;
		SQuaternion			operator /  ( float f ) const;
		// Unary operators
		SQuaternion			operator -( void ) const;
		SQuaternion			operator ~( void ) const;

		// 
		float				Element( uint8_t n ) const; // return n element from quaternion
		SQuaternion&		SetElement( uint8_t n, float fValue ); // set the n element to quaternion

		float				Dot( const SQuaternion& q ) const;
		float				Length( void ) const;
		float				SqLength( void ) const;

		void				AddScaledVector( const SVector4F& vec, float scale );
		SQuaternion&		AddScaledVector( const SVector3F& vec, float scale );

		void				SetRotation( const SQuaternion &q1, const SQuaternion &q2 );
		SVector3F			RotateVector( const SVector3F &v ) const;
		SQuaternion&		MakeFromEuler( float pitch, float yaw, float roll );
		SQuaternion&		MakeFromEuler( const SVector3F& v );
		SQuaternion&		LinearInterpolate( const SQuaternion &q1, const SQuaternion &q2, float fTime );
		SQuaternion&		SLERP( const SQuaternion &q1, const SQuaternion &q2, float fTime );

		void				GetEulers( float* pitch, float* yaw, float* roll );
		void				GetMatrix(SMatrix4x4 *m) const;
		void				GetMatrix(SMatrix3x3 *m) const;

		SQuaternion&		Normalize( void );
		void				Identity( void )
		{ x = y = z = 0.0f; w = 1.0f; }	
	};

	typedef struct SMatrix2x2
	{
		float	_11, _12;
		float	_21, _22;
	} SMatrix2;

	typedef struct SMatrix3x3
	{
		float	_11, _12, _13;
		float	_21, _22, _23;
		float	_31, _32, _33;
	} SMatrix3;

	typedef struct SMatrix4x4
	{
		float	_11, _12, _13, _14;
		float	_21, _22, _23, _24;
		float	_31, _32, _33, _34;
		float	_41, _42, _43, _44;
	} SMatrix4;

	struct SPlane
	{
		float x, y, z, d;
	};
#pragma pack (pop)

	static const SVector2F		VECTOR2_FRONT			= { 1.0f, 0.0f };
	static const SVector2F		VECTOR2_RIGHT			= { 0.0f, 1.0f };
	static const SVector2F		VECTOR2_ZERO			= { 0.0f, 0.0f };
	static const SVector2F		VECTOR2_ONE				= { 1.0f, 1.0f };
													 	// x	// y	// z
	static const SVector3F		VECTOR3_FRONT			= { 1.0f, 0.0f, 0.0f };
	static const SVector3F		VECTOR3_UP				= { 0.0f, 1.0f, 0.0f };
	static const SVector3F		VECTOR3_RIGHT			= { 0.0f, 0.0f, 1.0f };
	static const SVector3F		VECTOR3_ZERO			= { 0.0f, 0.0f, 0.0f };
	static const SVector3F		VECTOR3_ONE				= { 1.0f, 1.0f, 1.0f };
														// x	// y	// z	w
	static const SVector4F		VECTOR4_FRONT			= { 1.0f, 0.0f, 0.0f, 0.0f };
	static const SVector4F		VECTOR4_UP				= { 0.0f, 1.0f, 0.0f, 0.0f };
	static const SVector4F		VECTOR4_RIGHT			= { 0.0f, 0.0f, 1.0f, 0.0f };
	static const SVector4F		VECTOR4_ZERO			= { 0.0f, 0.0f, 0.0f, 1.0f };
	static const SVector4F		VECTOR4_ONE				= { 1.0f, 1.0f, 1.0f, 1.0f };

	static const SQuaternion	QUATERNION_IDENTITY		= { 0.0f, 0.0f, 0.0f, 1.0f };

	static const SMatrix2		MATRIX2_ZERO			= { 0 };
	static const SMatrix3		MATRIX3_ZERO			= { 0 };
	static const SMatrix4		MATRIX4_ZERO			= { 0 };
	static const SMatrix2		MATRIX2_ONE				= { 1.0f, 1.0f, 1.0f, 1.0f };
	static const SMatrix3		MATRIX3_ONE				= { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
	static const SMatrix4		MATRIX4_ONE				= { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f };
	static const SMatrix2		MATRIX2_IDENTITY		= { 1.0f, 0.0f, 
															0.0f, 1.0f };
	static const SMatrix3		MATRIX3_IDENTITY		= { 1.0f, 0.0f, 0.0f, 
															0.0f, 1.0f, 0.0f, 
															0.0f, 0.0f, 1.0f };
	static const SMatrix4		MATRIX4_IDENTITY		= { 1.0f, 0.0f, 0.0f, 0.0f, 
															0.0f, 1.0f, 0.0f, 0.0f, 
															0.0f, 0.0f, 1.0f, 0.0f, 
															0.0f, 0.0f, 0.0f, 1.0f };

	SVector2F*		VectorAdd						( SVector2F* out_vResult, const SVector2F* in_vA, const SVector2F* in_vB );
	SVector2F*		VectorAddInPlace				( SVector2F* inout_vResult, const SVector2F* in_vB );
	SVector2F*		VectorAddScaled					( SVector2F* out_vResult, const SVector2F* in_vA, const SVector2F* in_vB, float in_fBScale );
	SVector2F*		VectorAddScaledInPlace			( SVector2F* inout_vResult, const SVector2F* in_vB, float in_fBScale );
	SVector2F*		VectorSubstract					( SVector2F* out_vResult, const SVector2F* in_vA, const SVector2F* in_vB );
	SVector2F*		VectorSubstractInPlace			( SVector2F* inout_vResult, const SVector2F* in_vB );
	SVector2F*		VectorSubstractScaled			( SVector2F* out_vResult, const SVector2F* in_vA, const SVector2F* in_vB, float in_fBScale );
	SVector2F*		VectorSubstractScaledInPlace	( SVector2F* inout_vResult, const SVector2F* in_vB, float in_fBScale );
	SVector2F*		VectorMultiply					( SVector2F* out_vResult, const SVector2F* in_Vector, float in_fScalar );
	SVector2F*		VectorMultiplyInPlace			( SVector2F* inout_vResult, float in_fScalar );
	SVector2F*		VectorDivide					( SVector2F* out_vResult, const SVector2F* in_Vector, float in_fScalar );
	SVector2F*		VectorDivideInPlace				( SVector2F* inout_vResult, float in_fScalar );
	float			VectorDot						( const SVector2F* in_vA, const SVector2F* in_vB );
	SVector2F*		VectorNormalize					( SVector2F* out_vResult, const SVector2F* in_Vector );
	SVector2F*		VectorNormalizeInPlace			( SVector2F* inout_vResult );
	SVector2F*		VectorInterpolate				( SVector2F* inout_vResult, const SVector2F* in_vA, const SVector2F* in_vB, float fFactor );
	SVector2F*		VectorInterpolateInPlace		( SVector2F* inout_vResult, const SVector2F* in_vB, float fFactor );

	SVector3F*		VectorAdd						( SVector3F* out_vResult, const SVector3F* in_vA, const SVector3F* in_vB );
	SVector3F*		VectorAddInPlace				( SVector3F* inout_vResult, const SVector3F* in_vB );
	SVector3F*		VectorAddScaled					( SVector3F* out_vResult, const SVector3F* in_vA, const SVector3F* in_vB, float in_fBScale );
	SVector3F*		VectorAddScaledInPlace			( SVector3F* inout_vResult, const SVector3F* in_vB, float in_fBScale );
	SVector3F*		VectorSubstract					( SVector3F* out_vResult, const SVector3F* in_vA, const SVector3F* in_vB );
	SVector3F*		VectorSubstractInPlace			( SVector3F* inout_vResult, const SVector3F* in_vB );
	SVector3F*		VectorSubstractScaled			( SVector3F* out_vResult, const SVector3F* in_vA, const SVector3F* in_vB, float in_fBScale );
	SVector3F*		VectorSubstractScaledInPlace	( SVector3F* inout_vResult, const SVector3F* in_vB, float in_fBScale );
	SVector3F*		VectorMultiply					( SVector3F* out_vResult, const SVector3F* in_Vector, float in_fScalar );
	SVector3F*		VectorMultiplyInPlace			( SVector3F* inout_vResult, float in_fScalar );
	SVector3F*		VectorDivide					( SVector3F* out_vResult, const SVector3F* in_Vector, float in_fScalar );
	SVector3F*		VectorDivideInPlace				( SVector3F* inout_vResult, float in_fScalar );
	SVector3F*		VectorNormalize					( SVector3F* out_vResult, const SVector3F* in_Vector );
	SVector3F*		VectorNormalizeInPlace			( SVector3F* ininout_vResult );
	float			VectorDot						( const SVector3F* in_vA, const SVector3F* in_vB );
	SVector3F*		VectorCross						( SVector3F* out_vResult, const SVector3F* in_vLeft, const SVector3F* in_vRight );
	SVector3F*		VectorCrossAndNormalize			( SVector3F* out_vResult, const SVector3F* in_vLeft, const SVector3F* in_vRight );
	SVector3F*		VectorInterpolate				( SVector3F* inout_vResult, const SVector3F* in_vA, const SVector3F* in_vB, float fFactor );
	SVector3F*		VectorInterpolateInPlace		( SVector3F* inout_vResult, const SVector3F* in_vB, float fFactor );

	SVector4F*		VectorAdd						( SVector4F* out_vResult, const SVector4F* in_vA, const SVector4F* in_vB );
	SVector4F*		VectorAddInPlace				( SVector4F* inout_vResult, const SVector4F* in_vB );
	SVector4F*		VectorAddScaled					( SVector4F* out_vResult, const SVector4F* in_vA, const SVector4F* in_vB, float in_fBScale );
	SVector4F*		VectorAddScaledInPlace			( SVector4F* inout_vResult, const SVector4F* in_vB, float in_fBScale );
	SVector4F*		VectorSubstract					( SVector4F* out_vResult, const SVector4F* in_vA, const SVector4F* in_vB );
	SVector4F*		VectorSubstractInPlace			( SVector4F* inout_vResult, const SVector4F* in_vB );
	SVector4F*		VectorSubstractScaled			( SVector4F* out_vResult, const SVector4F* in_vA, const SVector4F* in_vB, float in_fBScale );
	SVector4F*		VectorSubstractScaledInPlace	( SVector4F* inout_vResult, const SVector4F* in_vB, float in_fBScale );
	SVector4F*		VectorMultiply					( SVector4F* out_vResult, const SVector4F* in_Vector, float in_fScalar );
	SVector4F*		VectorMultiplyInPlace			( SVector4F* inout_vResult, float in_fScalar );
	SVector4F*		VectorDivide					( SVector4F* out_vResult, const SVector4F* in_Vector, float in_fScalar );
	SVector4F*		VectorDivideInPlace				( SVector4F* inout_vResult, float in_fScalar );
	float			VectorDot						( const SVector4F* in_vA, const SVector4F* in_vB );
	SVector4F*		VectorNormalize					( SVector4F* out_vResult, const SVector4F* in_Vector );
	SVector4F*		VectorNormalizeInPlace			( SVector4F* inout_vResult );
	SVector4F*		VectorInterpolate				( SVector4F* out_vResult, const SVector4F* in_vA, const SVector4F* in_vB, float fFactor );
	SVector4F*		VectorInterpolateInPlace		( SVector4F* inout_vResult, const SVector4F* in_vB, float fFactor );

	SQuaternion*	QuaternionNormalize				( SQuaternion* out_vResult, const SQuaternion* in_vInput );
	SQuaternion*	QuaternionNormalizeInPlace		( SQuaternion* inout_vResult );
	SQuaternion*	QuaternionLinearInterpolate		( SQuaternion* out_vResult, const SQuaternion* in_vA, const SQuaternion* in_vB, float fFactor );

	SMatrix2x2*		MatrixAdd						( SMatrix2x2* out_mResult, const SMatrix2x2* in_mLeft, const SMatrix2x2* in_mRight );
	SMatrix2x2*		MatrixSubstract					( SMatrix2x2* out_mResult, const SMatrix2x2* in_mLeft, const SMatrix2x2* in_mRight );
	SMatrix2x2*		MatrixMultiply					( SMatrix2x2* out_mResult, const SMatrix2x2* in_mLeft, const SMatrix2x2* in_mRight );
	SMatrix2x2*		MatrixInverse					( SMatrix2x2* out_mResult, float* out_fDeterminant, const SMatrix2x2* in_Matrix);
	SMatrix2x2*		MatrixTranspose					( SMatrix2x2* out_mResult, const SMatrix2x2* in_Matrix);
	SMatrix2x2*		MatrixTransposeInPlace			( SMatrix2x2* inout_mResult );
	SMatrix2x2*		MatrixMultiply					( SMatrix2x2* out_mResult, const SMatrix2x2* in_mLeft, float in_fScalar);
	SMatrix2x2*		MatrixDivide					( SMatrix2x2* out_mResult, const SMatrix2x2* in_mLeft, float in_fScalar);
	SVector2F*		MatrixTransform					( SVector2F* out_vResult, const SVector2F* in_vLeft, const SMatrix2x2* in_mRight );
	SVector2F*		MatrixTransformInPlace			( SVector2F* out_vResult, const SMatrix2x2* in_mRight );

	SMatrix3x3*		MatrixAdd						( SMatrix3x3* out_mResult, const SMatrix3x3* in_mLeft, const SMatrix3x3* in_mRight );
	SMatrix3x3*		MatrixSubstract					( SMatrix3x3* out_mResult, const SMatrix3x3* in_mLeft, const SMatrix3x3* in_mRight );
	SMatrix3x3*		MatrixMultiply					( SMatrix3x3* out_mResult, const SMatrix3x3* in_mLeft, const SMatrix3x3* in_mRight );
	SMatrix3x3*		MatrixInverse					( SMatrix3x3* out_mResult, float* out_fDeterminant, const SMatrix3x3* in_Matrix );
	SMatrix3x3*		MatrixTranspose					( SMatrix3x3* out_mResult, const SMatrix3x3* in_Matrix );
	SMatrix3x3*		MatrixTransposeInPlace			( SMatrix3x3* inout_mResult );
	SMatrix3x3*		MatrixMultiply					( SMatrix3x3* out_mResult, const SMatrix3x3* in_mLeft, float in_fScalar );
	SMatrix3x3*		MatrixDivide					( SMatrix3x3* out_mResult, const SMatrix3x3* in_mLeft, float in_fScalar );
	SVector3F*		MatrixTransform					( SVector3F* out_vResult, const SVector3F* in_vLeft, const SMatrix3x3* in_mRight );
	SVector4F*		MatrixTransform					( SVector4F* out_vResult, const SVector4F* in_vLeft, const SMatrix3x3* in_mRight );
	SVector3F*		MatrixTransformInPlace			( SVector3F* out_vResult, const SMatrix3x3* in_mRight );
	SVector4F*		MatrixTransformInPlace			( SVector4F* out_vResult, const SMatrix3x3* in_mRight );

	SMatrix4x4*		MatrixAdd						( SMatrix4x4* out_mResult, const SMatrix4x4* in_mLeft, const SMatrix4x4* in_mRight );
	SMatrix4x4*		MatrixSubstract					( SMatrix4x4* out_mResult, const SMatrix4x4* in_mLeft, const SMatrix4x4* in_mRight );
	SMatrix4x4*		MatrixMultiply					( SMatrix4x4* out_mResult, const SMatrix4x4* in_mLeft, const SMatrix4x4* in_mRight );
	SMatrix4x4*		MatrixInverse					( SMatrix4x4* out_mResult, float* out_fDeterminant, const SMatrix4x4* in_Matrix );
	SMatrix4x4*		MatrixInverseInPlace			( SMatrix4x4* out_mResult, float* out_fDeterminant );
	SMatrix4x4*		MatrixTranspose					( SMatrix4x4* out_mResult, const SMatrix4x4* in_Matrix );
	SMatrix4x4*		MatrixTransposeInPlace			( SMatrix4x4* inout_mResult );
	SMatrix4x4*		MatrixMultiply					( SMatrix4x4* out_mResult, const SMatrix4x4* in_mLeft, float in_fScalar );
	SMatrix4x4*		MatrixDivide					( SMatrix4x4* out_mResult, const SMatrix4x4* in_mLeft, float in_fScalar );
	SVector3F*		MatrixTransform					( SVector3F* out_vResult, const SVector3F* in_vLeft, const SMatrix4x4* in_mRight );
	SVector4F*		MatrixTransform					( SVector4F* out_vResult, const SVector4F* in_vLeft, const SMatrix4x4* in_mRight );
	SVector3F*		MatrixTransformInPlace			( SVector3F* out_vResult, const SMatrix4x4* in_mRight );
	SVector4F*		MatrixTransformInPlace			( SVector4F* out_vResult, const SMatrix4x4* in_mRight );

	//bool operator ==( const SVector2F& a	, const SVector2F& b );
	//bool operator ==( const SVector3F& a	, const SVector3F& b );
	//bool operator ==( const SVector4F& a	, const SVector4F& b );
	//bool operator ==( const SPlane& a		, const SPlane& b );
	//bool operator !=( const SVector2F& a	, const SVector2F& b );
	//bool operator !=( const SVector3F& a	, const SVector3F& b );
	//bool operator !=( const SVector4F& a	, const SVector4F& b );
	//bool operator !=( const SPlane& a		, const SPlane& b );

};

#endif // _M3DPOINT_H